﻿module game.units;

public import gamelib.linalg;

enum InitialCacheSize = 64;

auto calcCacheSize(U)(U size)
{
    import gamelib.math;
    return max(uppow2(cast(U)InitialCacheSize), uppow2(size));
}

static if(false)
{
    public import std.math;
    alias float scalar_t;
    alias scalar_t scalarw_t;
    alias vec2 rot_t;
    alias mat2 transform_mat_t;
    alias vec2 pos_t;
}
else
{
    public import gamelib.fixedpoint;
    import gamelib.util: Widen;
    alias FixedPoint!(13,19,int) scalar_t;
    alias Widen!scalar_t scalarw_t;
    alias Vector!(scalar_t,2) rot_t;
    alias Matrix!(scalar_t,2,2) transform_mat_t;
    alias Vector!(scalar_t,2) pos_t;
    unittest
    {
        testFixedPointFuncs!scalar_t();
    }
}

enum scalar_t ZeroScalar = cast(scalar_t)0;
enum scalar_t OneScalar  = cast(scalar_t)1;
enum rot_t ZeroRot = rot_t(OneScalar,  ZeroScalar);
enum rot_t Pi2Rot  = rot_t(ZeroScalar, OneScalar);
enum pos_t ZeroPos = pos_t(ZeroScalar, ZeroScalar);

scalar_t div(int l,int r) pure nothrow
{
    return cast(scalar_t)l/cast(scalar_t)r;
}

auto toRotMat(in rot_t rot) pure nothrow
in
{
    import gamelib.math: almost_equal;
    import std.conv;
    assert(rot.x > -(1 + scalar_t.epsilon * 5));
    assert(rot.x <  (1 + scalar_t.epsilon * 5));
    assert(rot.y > -(1 + scalar_t.epsilon * 5));
    assert(rot.y <  (1 + scalar_t.epsilon * 5));
    assert(almost_equal(hypot(rot.x,rot.y),cast(scalar_t)1, (scalar_t.epsilon * 5)));
}
body
{
    return transform_mat_t(rot.x, -rot.y,
                           rot.y,  rot.x);
}
auto toRotMat(in scalar_t rot) pure nothrow
{
    auto s = sin(rot);
    auto c = cos(rot);
    return toRotMat(rot_t(c, s));
}
float toRadians(in rot_t rot) pure nothrow
{
    return cast(float)atan2(rot.y, rot.x); 
}
float toDegrees(in rot_t rot) pure nothrow
{
    import std.math: PI;
    return toRadians(rot) / (PI * 2.0f) * 360.0f;
}
